﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.Entity;
using System.Linq.Expressions;
using AppManage.Infrastructure.Entity;

namespace AppManage.Infrastructure.Repository
{
    public class RepositoryBase<TDbContext, TEntity, TPrimaryKey> : IRepository<TEntity, TPrimaryKey>
        where TDbContext : DbContext
        where TEntity : class, IEntity<TPrimaryKey>
    {
        public RepositoryBase(DbContextProvider<TDbContext> provider)
        {
            DbContext = provider.DbContext;
        }

        public TDbContext DbContext { get; }

        public TDbContext GetDbContext()
        {
            return DbContext;
        }

        public virtual DbSet<TEntity> Table { get { return DbContext.Set<TEntity>(); } }

        public int Count()
        {
            return Table.Count();
        }

        public int Count(Expression<Func<TEntity, bool>> predicate)
        {
            return Table.Count(predicate);
        }

        public void Delete(TEntity entity)
        {
            AttachIfNot(entity);
            Table.Remove(entity);
            DbContext.SaveChanges();
        }

        public void Delete(TPrimaryKey id)
        {
            var entity = Table.Local.FirstOrDefault(ent => EqualityComparer<TPrimaryKey>.Default.Equals(ent.Id, id));
            if (entity == null)
            {
                entity = FirstOrDefault(id);
                if (entity == null)
                {
                    return;
                }
            }
            Delete(entity);
        }

        public TPrimaryKey InsertAndGetId(TEntity entity)
        {
            entity = Table.Add(entity);
            DbContext.SaveChanges();
            return entity.Id;
        }

        public int Update(TEntity entity)
        {
            AttachIfNot(entity);
            DbContext.Entry(entity).State = EntityState.Modified;
            return DbContext.SaveChanges();
        }
        public IQueryable<TEntity> GetAll()
        {
            return Table;
        }

        public IQueryable<TEntity> GetAllIncluding(params Expression<Func<TEntity, object>>[] propertySelectors)
        {
            if (propertySelectors == null || propertySelectors.Count() < 0)
            {
                return GetAll();
            }
            var query = GetAll();
            foreach (var propertySelector in propertySelectors)
            {
                query = query.Include(propertySelector);
            }
            return query;
        }

        public List<TEntity> GetAllList()
        {
            return GetAll().ToList();
        }

        public TEntity FirstOrDefault(TPrimaryKey id)
        {
            return Table.FirstOrDefault(ent => EqualityComparer<TPrimaryKey>.Default.Equals(ent.Id, id));
        }

        protected void AttachIfNot(TEntity entity)
        {
            if (Table.Local.Contains(entity))
            {
                Table.Attach(entity);
            }
        }

    }
}
